<!-- 文件路径：chatapp-frontend/src/views/Chat.vue -->
<template>
  <div class="chat-container">
    <!-- (左侧侧栏代码无变化，此处省略以减少重复) -->
    <div class="chat-sidebar">
      <div class="sidebar-header">
        <h1 class="app-title">
          艺<span class="title-accent">语</span>
        </h1>
        <p class="app-subtitle">体验沉浸式艺术交流</p>
      </div>

      <div class="user-info-section">
        <div class="user-avatar-main">
          <img
              v-if="currentUser.avatar"
              :src="currentUser.avatar"
              class="avatar-img"
              alt="用户头像"
          />
          <div v-else class="avatar-default">
            <span class="avatar-initial-large">{{ currentUser.nickname ? currentUser.nickname.charAt(0).toUpperCase() : '?' }}</span>
          </div>
          <label for="avatarInput" class="avatar-upload-overlay">
            <i class="fas fa-camera-retro"></i>
          </label>
          <input
              id="avatarInput"
              type="file"
              accept="image/*"
              @change="uploadAvatar"
              class="avatar-input-hidden"
          />
        </div>

        <div class="user-details">
          <div class="username-display" :title="currentUser.username">{{ currentUser.username }}</div>
          <div v-if="!editingNickname" class="nickname-display">
            <span :title="currentUser.nickname">{{ currentUser.nickname }}</span>
            <button class="edit-action-btn" @click="startEditNickname" title="编辑昵称">
              <i class="fas fa-paint-brush"></i>
            </button>
          </div>
          <div v-else class="nickname-edit-form">
            <input
                type="text"
                v-model="newNickname"
                class="nickname-input-field"
                placeholder="输入新昵称"
                @keyup.enter="confirmNickname"
                @keyup.esc="cancelEditNickname"
                ref="nicknameInputRef"
            />
            <button class="confirm-action-btn" @click="confirmNickname" title="确认">
              <i class="fas fa-check-circle"></i>
            </button>
            <button class="cancel-action-btn" @click="cancelEditNickname" title="取消">
              <i class="fas fa-times-circle"></i>
            </button>
          </div>
        </div>
      </div>

      <div class="online-users-section">
        <h3 class="section-title">
          <i class="fas fa-users-cog"></i> 在线画友
        </h3>
        <div class="user-list-scrollable">
          <div
              v-for="userObj in sortedUsers"
              :key="userObj.username"
              class="online-user-item"
              :class="{
          'user-active': onlineNicknames.includes(userObj.nickname),
          'user-inactive': !onlineNicknames.includes(userObj.nickname)
          }"
              :title="userObj.nickname + (onlineNicknames.includes(userObj.nickname) ? ' (在线)' : ' (离线)')"
          >
            <div class="user-avatar-small">
              <img
                  v-if="userObj.avatar"
                  :src="fullAvatarUrl(userObj.avatar)"
                  class="avatar-small-img"
                  :alt="userObj.nickname + '头像'"
              />
              <div v-else class="avatar-small-default">
                <span class="avatar-initial-small">{{ userObj.nickname ? userObj.nickname.charAt(0).toUpperCase() : '?' }}</span>
              </div>
              <span class="status-indicator" :class="{'status-online': onlineNicknames.includes(userObj.nickname)}"></span>
            </div>
            <div class="user-item-name">{{ userObj.nickname }}</div>
            <i class="fas fa-xs fa-comment-dots user-action-indicator"></i>
          </div>
          <p v-if="!sortedUsers.length && allUsers.length > 1" class="no-users-text">没有其他在线画友</p>
          <p v-if="allUsers.length <= 1" class="no-users-text">等待其他画友加入...</p>
        </div>
      </div>

      <div class="sidebar-footer">
        <button class="logout-action-button" @click="handleLogout">
          <i class="fas fa-sign-out-alt"></i>
          <span>退出艺廊</span>
        </button>
      </div>
    </div>


    <!-- 右侧主区域：消息列表 + 输入框 -->
    <div class="chat-main-area">
      <div class="message-list-container" ref="messageList">
        <template v-for="(msg, index) in messages" :key="msg.id || `${msg.sender}-${msg.timestamp}-${index}`">
          <ArtisticMessage
              v-if="msg.messageType === 'USER' || msg.messageType === 'IMAGE'"
              :msg="msg"
              :avatar="senderAvatarMap[msg.sender] || null"
              :style="{'--index': index}"
              @image-preview-requested="showImageInModal"
          />
          <!-- 原本的错误注释已被修正/移除 -->
          <div v-else-if="msg.messageType === 'SYSTEM_NICKNAME_CHANGE'" class="system-message">
            <i class="fas fa-info-circle system-icon"></i>
            <span>{{ formatSystemTimestamp(msg.timestamp) }} - {{ msg.content }}</span>
          </div>
        </template>
        <p v-if="!messages.length && isConnected" class="no-messages-prompt">
          <i class="fas fa-comments"></i> 开始你的艺术对话吧！
        </p>
        <p v-if="!isConnected" class="no-messages-prompt">
          <i class="fas fa-satellite-dish"></i> 正在连接灵感之源...
        </p>
      </div>

      <div class="chat-input-area">
        <div class="input-field-container" :class="{'focused': isInputFocused}">
          <textarea
              ref="chatInput"
              v-model="draftMessage"
              placeholder="挥洒你的灵感... (Ctrl+Enter 发送)"
              @keydown="handleTextareaKeydown"
              @input="autoGrowTextarea"
              :disabled="!isConnected || isUploadingImage"
              rows="1"
              @focus="isInputFocused = true"
              @blur="isInputFocused = false"
          ></textarea>
          <button @click="triggerImageUpload" class="image-upload-button" title="发送图片" :disabled="!isConnected || isUploadingImage">
            <i class="fas fa-paperclip"></i>
          </button>
          <input
              type="file"
              ref="imageUploadInput"
              @change="handleImageSelected"
              accept="image/*"
              style="display: none;"
          />
          <button
              @click="sendTextMessage"
              class="send-action-button"
              :disabled="!isConnected || !draftMessage.trim() || isUploadingImage"
              title="发送消息 (Ctrl+Enter)"
          >
            <i class="fas fa-feather-alt"></i>
          </button>
        </div>
        <div v-if="imageUploadError" class="upload-error-message">
          {{ imageUploadError }}
        </div>
      </div>
    </div>

    <!-- Image Modal Structure -->
    <div v-if="showImageModal" class="image-modal-overlay" @click="closeImageModal">
      <div class="image-modal-content" @click.stop>
        <img :src="currentModalImageUrl" alt="聊天图片放大预览" class="modal-image-display"/>
        <button @click="closeImageModal" class="modal-close-button" title="关闭">×</button>
      </div>
    </div>

  </div>
</template>

<script>
import { ref, onMounted, onBeforeUnmount, nextTick, computed } from 'vue'
import { useRouter } from 'vue-router'
import SockJS from 'sockjs-client'
import Stomp from 'stompjs'
import axios from 'axios'
import ArtisticMessage from '@/components/ArtisticMessage.vue'

export default {
  name: 'Chat',
  components: { ArtisticMessage },
  setup() {
    const router = useRouter()
    const currentUser = ref(JSON.parse(localStorage.getItem('currentUser') || '{}'))
    const allUsers = ref([])
    const prevAllUsers = ref([])
    const onlineNicknames = ref([])
    const senderAvatarMap = ref({})
    const messages = ref([])
    const draftMessage = ref('')
    const stompClient = ref(null)
    const isConnected = ref(false)
    const messageList = ref(null)
    const editingNickname = ref(false)
    const newNickname = ref(currentUser.value?.nickname || '')
    const chatInput = ref(null);
    const nicknameInputRef = ref(null);
    const isInputFocused = ref(false);

    const imageUploadInput = ref(null);
    const isUploadingImage = ref(false);
    const imageUploadError = ref('');

    const showImageModal = ref(false);
    const currentModalImageUrl = ref('');


    const backendProtocol = window.location.protocol;
    const backendHostname = window.location.hostname;
    const backendPort = '8080';
    const backendOrigin = `${backendProtocol}//${backendHostname}:${backendPort}`;


    const updateAvatarMap = () => {
      const map = {}
      allUsers.value.forEach((u) => {
        map[u.nickname] = u.avatar ? fullAvatarUrl(u.avatar) : null
      })
      senderAvatarMap.value = map
    }

    const autoGrowTextarea = () => {
      if (chatInput.value) {
        chatInput.value.style.height = 'auto';
        const newHeight = Math.max(chatInput.value.scrollHeight, 22);
        chatInput.value.style.height = newHeight + 'px';
      }
    };

    const fullAvatarUrl = (avatarPath) => {
      if (!avatarPath) return null;
      return avatarPath.startsWith('/avatars/') ? `${backendOrigin}${avatarPath}` : avatarPath
    }

    const fullChatImageUrl = (imagePath) => {
      if (!imagePath) return null;
      return imagePath.startsWith('/chat-images/') ? `${backendOrigin}${imagePath}` : imagePath;
    };


    if (currentUser.value && currentUser.value.avatar) {
      currentUser.value.avatar = fullAvatarUrl(currentUser.value.avatar);
    }


    const sortedUsers = computed(() => {
      return [...allUsers.value]
          .filter(u => u.username !== currentUser.value.username)
          .sort((a, b) => {
            const aOnline = onlineNicknames.value.includes(a.nickname);
            const bOnline = onlineNicknames.value.includes(b.nickname);
            if (aOnline && !bOnline) return -1;
            if (!aOnline && bOnline) return 1;
            return a.nickname.localeCompare(b.nickname, 'zh-CN');
          });
    });

    const scrollToBottom = (force = false) => {
      nextTick(() => {
        if (messageList.value) {
          const { scrollTop, scrollHeight, clientHeight } = messageList.value;
          if (force || scrollHeight - scrollTop - clientHeight < 250) {
            messageList.value.scrollTop = scrollHeight;
          }
        }
      })
    }

    const formatSystemTimestamp = (timestampStr) => {
      if (!timestampStr) return '';
      let dateInput = timestampStr;
      if (typeof timestampStr === 'string') {
        const hasZ = timestampStr.endsWith('Z');
        const hasOffset = /([+\-])\d{2}:\d{2}$/.test(timestampStr);
        if (!hasZ && !hasOffset) {
          if (timestampStr.includes('T')) {
            dateInput = timestampStr + 'Z';
          } else if (timestampStr.includes(' ') && timestampStr.includes(':') && timestampStr.includes('-')) {
            dateInput = timestampStr.replace(' ', 'T') + 'Z';
          }
        }
      }
      const date = new Date(dateInput);
      if (isNaN(date.getTime())) return "时间未知";
      return date.toLocaleTimeString('zh-CN', {
        timeZone: 'Asia/Shanghai',
        hour: '2-digit',
        minute: '2-digit',
        hour12: false
      });
    };


    const loadHistory = async () => {
      try {
        const resp = await axios.get(`${backendOrigin}/api/history`)
        messages.value = resp.data.map((dbMsg) => {
          let processedContent = dbMsg.content;
          if (dbMsg.messageType === 'IMAGE' && dbMsg.content) {
            processedContent = fullChatImageUrl(dbMsg.content);
          }

          if (dbMsg.messageType === 'SYSTEM_NICKNAME_CHANGE') {
            return {
              ...dbMsg,
              id: dbMsg.id,
              content: processedContent,
            };
          } else {
            const isMe = dbMsg.sender === currentUser.value.nickname;
            return {
              ...dbMsg,
              id: dbMsg.id,
              content: processedContent,
              displaySender: isMe ? '我' : dbMsg.sender,
              isSent: isMe,
              messageType: dbMsg.messageType || 'USER'
            };
          }
        });
        scrollToBottom(true)
      } catch (e) {
        console.error('加载历史消息失败', e)
      }
    }

    const connectWebSocket = () => {
      const socket = new SockJS(`${backendOrigin}/ws-chat`);
      stompClient.value = Stomp.over(socket)
      stompClient.value.debug = null;

      stompClient.value.connect(
          {},
          () => {
            isConnected.value = true
            console.log('WebSocket 已连接')

            stompClient.value.subscribe('/topic/public', (frame) => {
              const msgFromServer = JSON.parse(frame.body);
              let displayContent = msgFromServer.content;

              if (msgFromServer.messageType === 'IMAGE' && msgFromServer.content) {
                displayContent = fullChatImageUrl(msgFromServer.content);
              }

              let formattedMsg;

              if (msgFromServer.messageType === 'SYSTEM_NICKNAME_CHANGE') {
                formattedMsg = {
                  ...msgFromServer,
                  id: msgFromServer.id || `system-${Date.now()}-${Math.random()}`,
                  content: displayContent,
                };
                messages.value.push(formattedMsg);

                const nicknameChangeRegex = /“(.+?)” 已将昵称修改为 “(.+?)”/;
                const match = msgFromServer.content.match(nicknameChangeRegex);
                if (match) {
                  const oldNickInSystemMsg = match[1];
                  const newNickInSystemMsg = match[2];
                  if (currentUser.value.nickname === oldNickInSystemMsg && currentUser.value.username !== "系统提示") {
                    const localUserCopy = JSON.parse(localStorage.getItem('currentUser') || '{}');
                    if(localUserCopy.nickname === oldNickInSystemMsg) {
                      localUserCopy.nickname = newNickInSystemMsg;
                      currentUser.value.nickname = newNickInSystemMsg;
                      localStorage.setItem('currentUser', JSON.stringify(localUserCopy));
                    }
                  }
                }
                scrollToBottom();

              } else {
                const isMe = msgFromServer.sender === currentUser.value.nickname;
                formattedMsg = {
                  ...msgFromServer,
                  content: displayContent,
                  displaySender: isMe ? '我' : msgFromServer.sender,
                  isSent: isMe,
                  messageType: msgFromServer.messageType || 'USER'
                };

                if (isMe) {
                  const optimisticMatch = messages.value.find(
                      (m) => m.isSent &&
                          m.id && m.id.toString().startsWith('optimistic-') &&
                          (
                              (m.messageType === 'USER' && m.tempContentForMatch === msgFromServer.content) ||
                              (m.messageType === 'IMAGE' && m.originalRelativePath === msgFromServer.content)
                          ) &&
                          m.sender === msgFromServer.sender
                  );

                  if (optimisticMatch) {
                    optimisticMatch.id = msgFromServer.id;
                    optimisticMatch.timestamp = msgFromServer.timestamp;
                    if (msgFromServer.messageType) {
                      optimisticMatch.messageType = msgFromServer.messageType;
                    }
                    delete optimisticMatch.tempContentForMatch;
                    delete optimisticMatch.originalRelativePath;
                  } else {
                    if (messages.value.findIndex(existingMsg => existingMsg.id === msgFromServer.id) === -1) {
                      messages.value.push(formattedMsg);
                      scrollToBottom();
                    }
                  }
                } else {
                  messages.value.push(formattedMsg);
                  scrollToBottom();
                }
              }
            });


            stompClient.value.subscribe('/topic/onlineUsers', (frame) => {
              onlineNicknames.value = JSON.parse(frame.body)
            })

            stompClient.value.subscribe('/topic/allUsers', (frame) => {
              const oldList = JSON.parse(JSON.stringify(prevAllUsers.value));
              const newList = JSON.parse(frame.body)

              newList.forEach((newUser) => {
                const oldUser = oldList.find((u) => u.username === newUser.username)
                if (oldUser && oldUser.nickname && newUser.nickname && oldUser.nickname !== newUser.nickname) {
                  const oldNick = oldUser.nickname
                  const newNick = newUser.nickname

                  messages.value.forEach((msg) => {
                    if ((msg.messageType === 'USER' || msg.messageType === 'IMAGE') && msg.sender === oldNick) {
                      msg.sender = newNick;
                      if (msg.displaySender === oldNick || (msg.displaySender === "我" && currentUser.value.username === newUser.username) ) {
                        msg.displaySender = (currentUser.value.username === newUser.username) ? "我" : newNick;
                      }
                    }
                  });

                  if(currentUser.value.username === newUser.username) {
                    currentUser.value.nickname = newNick;
                    const localUserCopy = JSON.parse(localStorage.getItem('currentUser') || '{}');
                    localUserCopy.nickname = newNick;
                    localStorage.setItem('currentUser', JSON.stringify(localUserCopy));
                  }
                }
              })

              allUsers.value = newList
              prevAllUsers.value = JSON.parse(JSON.stringify(newList));
              updateAvatarMap()

              const me = allUsers.value.find((u) => u.username === currentUser.value.username)
              if (me) {
                const newAvatarUrl = me.avatar ? fullAvatarUrl(me.avatar) : null;
                const localUserCopy = JSON.parse(localStorage.getItem('currentUser') || '{}');

                let changedInLocal = false;
                if(localUserCopy.nickname !== me.nickname) {
                  localUserCopy.nickname = me.nickname;
                  changedInLocal = true;
                }
                if (localUserCopy.avatar !== newAvatarUrl) {
                  localUserCopy.avatar = newAvatarUrl;
                  changedInLocal = true;
                }

                if(currentUser.value.nickname !== me.nickname) currentUser.value.nickname = me.nickname;
                if(currentUser.value.avatar !== newAvatarUrl) currentUser.value.avatar = newAvatarUrl;

                if (changedInLocal) {
                  localStorage.setItem('currentUser', JSON.stringify(localUserCopy));
                }
              }
            })

            stompClient.value.send('/app/online', {}, JSON.stringify({ username: currentUser.value.nickname }))
            loadHistory()
          },
          (err) => {
            console.error('WebSocket 连接失败', err)
            isConnected.value = false;
            setTimeout(connectWebSocket, 5000)
          }
      )
    }

    const sendTextMessage = () => {
      if (!draftMessage.value.trim() || !isConnected.value) return
      const now = new Date().toISOString()
      const textContent = draftMessage.value.trim();

      const msgToServer = {
        content: textContent,
        sender: currentUser.value.nickname,
        timestamp: now,
        messageType: 'USER'
      }

      messages.value.push({
        ...msgToServer,
        id: `optimistic-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        displaySender: '我',
        isSent: true,
        tempContentForMatch: textContent,
      })
      scrollToBottom(true)

      if (stompClient.value && stompClient.value.connected) {
        stompClient.value.send('/app/chat', {}, JSON.stringify(msgToServer));
      } else {
        console.error("无法发送文本消息：WebSocket未连接");
      }
      draftMessage.value = ''
      nextTick(() => {
        if (chatInput.value) {
          autoGrowTextarea();
          chatInput.value.focus();
        }
      });
    }

    const sendImageMessage = (relativeImageUrl) => {
      if (!relativeImageUrl || !isConnected.value) return;
      const now = new Date().toISOString();

      const msgToServer = {
        content: relativeImageUrl,
        sender: currentUser.value.nickname,
        timestamp: now,
        messageType: 'IMAGE'
      };

      messages.value.push({
        content: fullChatImageUrl(relativeImageUrl),
        sender: currentUser.value.nickname,
        timestamp: now,
        messageType: 'IMAGE',
        id: `optimistic-img-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        displaySender: '我',
        isSent: true,
        originalRelativePath: relativeImageUrl,
      });
      scrollToBottom(true);

      if (stompClient.value && stompClient.value.connected) {
        stompClient.value.send('/app/chat', {}, JSON.stringify(msgToServer));
      } else {
        console.error("无法发送图片消息：WebSocket未连接");
        imageUploadError.value = "连接已断开，图片发送失败。";
      }
    };

    const triggerImageUpload = () => {
      if (imageUploadInput.value) {
        imageUploadInput.value.click();
      }
    };

    const handleImageSelected = async (event) => {
      const file = event.target.files[0];
      if (!file) return;

      if (file.size > 5 * 1024 * 1024) {
        imageUploadError.value = '图片文件过大，请选择5MB以下的图片。';
        if(imageUploadInput.value) imageUploadInput.value.value = null;
        return;
      }
      if (!['image/jpeg', 'image/png', 'image/gif', 'image/webp'].includes(file.type)) {
        imageUploadError.value = '不支持的图片格式。请选择JPG, PNG, GIF或WEBP图片。';
        if(imageUploadInput.value) imageUploadInput.value.value = null;
        return;
      }


      isUploadingImage.value = true;
      imageUploadError.value = '';

      const formData = new FormData();
      formData.append('file', file);

      try {
        const response = await axios.post(`${backendOrigin}/api/users/chat-image/upload`, formData, {
          headers: { 'Content-Type': 'multipart/form-data' }
        });

        const { imageUrl } = response.data;
        if (imageUrl) {
          sendImageMessage(imageUrl);
        } else {
          throw new Error("上传成功但未收到图片URL");
        }

      } catch (error) {
        console.error('上传聊天图片失败', error);
        imageUploadError.value = error.response?.data?.error || "图片上传失败，请稍后再试。";
      } finally {
        isUploadingImage.value = false;
        if (imageUploadInput.value) {
          imageUploadInput.value.value = null;
        }
      }
    };


    const handleTextareaKeydown = (event) => {
      if (event.key === 'Enter' && event.ctrlKey) {
        event.preventDefault();
        sendTextMessage();
      }
    };


    const handleLogout = () => {
      if (stompClient.value && stompClient.value.connected) {
        stompClient.value.send('/app/offline', {}, JSON.stringify({ username: currentUser.value.nickname }))
        stompClient.value.disconnect(() => {
          console.log("WebSocket 已断开")
        })
      }
      localStorage.removeItem('currentUser')
      router.push('/login')
    }

    const startEditNickname = () => {
      editingNickname.value = true
      newNickname.value = currentUser.value.nickname
      nextTick(() => {
        if(nicknameInputRef.value) nicknameInputRef.value.focus();
      });
    }

    const confirmNickname = async () => {
      const trimmedNewNickname = newNickname.value.trim();
      if (!trimmedNewNickname || trimmedNewNickname === currentUser.value.nickname) {
        editingNickname.value = false;
        newNickname.value = currentUser.value.nickname;
        return;
      }

      const localOldNickname = currentUser.value.nickname;

      try {
        await axios.put(`${backendOrigin}/api/users/nickname`, {
          username: currentUser.value.username,
          newNickname: trimmedNewNickname
        });

        if (stompClient.value && stompClient.value.connected) {
          stompClient.value.send('/app/offline', {}, JSON.stringify({ username: localOldNickname }));
          stompClient.value.send('/app/online', {}, JSON.stringify({ username: trimmedNewNickname }));
        }
        editingNickname.value = false;
      } catch (error) {
        console.error('更新昵称失败', error);
        alert(error.response?.data || "更新昵称失败，请稍后再试。");
        newNickname.value = localOldNickname;
        editingNickname.value = false;
      }
    };


    const cancelEditNickname = () => {
      editingNickname.value = false
      newNickname.value = currentUser.value.nickname
    }

    const uploadAvatar = async (event) => {
      const file = event.target.files[0]
      if (!file) return

      const formData = new FormData()
      formData.append('username', currentUser.value.username)
      formData.append('file', file)

      try {
        await axios.post(`${backendOrigin}/api/users/avatar`, formData, {
          headers: { 'Content-Type': 'multipart/form-data' }
        })
        event.target.value = null;
      } catch (error) {
        console.error('上传头像失败', error)
        alert(error.response?.data?.error || "上传头像失败");
        event.target.value = null;
      }
    }

    const showImageInModal = (imageUrl) => {
      currentModalImageUrl.value = imageUrl;
      showImageModal.value = true;
    };

    const closeImageModal = () => {
      showImageModal.value = false;
      currentModalImageUrl.value = '';
    };


    onMounted(() => {
      if (!currentUser.value || !currentUser.value.username) {
        router.push('/login')
        return;
      }
      axios.get(`${backendOrigin}/api/users/allFull`)
          .then((r) => {
            allUsers.value = r.data;
            prevAllUsers.value = JSON.parse(JSON.stringify(r.data));

            const me = allUsers.value.find(u => u.username === currentUser.value.username);
            if (me) {
              let changed = false;
              if(currentUser.value.nickname !== me.nickname) {
                currentUser.value.nickname = me.nickname;
                changed = true;
              }
              const serverAvatarFullUrl = me.avatar ? fullAvatarUrl(me.avatar) : null;
              if(currentUser.value.avatar !== serverAvatarFullUrl){
                currentUser.value.avatar = serverAvatarFullUrl;
                changed = true;
              }
              if(changed){
                localStorage.setItem('currentUser', JSON.stringify(currentUser.value));
              }
            } else {
              console.warn("Current user not found in initial allFull list. Using localStorage data.");
            }
            updateAvatarMap();
            connectWebSocket();
          })
          .catch((e) => {
            console.error('获取所有用户列表失败', e);
            if (e.response && (e.response.status === 401 || e.response.status === 403) ) {
              handleLogout();
            } else {
              alert("无法获取完整的用户列表，部分功能可能受限。");
              connectWebSocket();
            }
          });
      nextTick(autoGrowTextarea);
    })

    onBeforeUnmount(() => {
      if (stompClient.value && stompClient.value.connected) {
        stompClient.value.send('/app/offline', {}, JSON.stringify({ username: currentUser.value.nickname }))
        stompClient.value.disconnect()
      }
    })

    return {
      currentUser, allUsers, onlineNicknames, senderAvatarMap, messages, draftMessage,
      isConnected, sortedUsers, messageList, editingNickname, newNickname, nicknameInputRef,
      sendTextMessage, handleLogout, startEditNickname, confirmNickname, cancelEditNickname,
      uploadAvatar, fullAvatarUrl, chatInput, autoGrowTextarea,
      handleTextareaKeydown,
      formatSystemTimestamp,
      isInputFocused,

      imageUploadInput,
      triggerImageUpload,
      handleImageSelected,
      isUploadingImage,
      imageUploadError,

      showImageModal,
      currentModalImageUrl,
      showImageInModal,
      closeImageModal
    }
  }
}
</script>

<style scoped>
/* System messages: more prominent and stylized */
.system-message {
  text-align: center;
  color: #b0c4de;
  font-size: 0.9rem;
  padding: 10px 15px;
  margin: 8px auto;
  font-style: normal;
  background: linear-gradient(135deg, rgba(76, 201, 240, 0.15), rgba(114, 9, 183, 0.15));
  border-radius: 8px;
  max-width: 85%;
  box-shadow: 0 2px 8px rgba(0,0,0,0.2), inset 0 0 10px rgba(200,200,255,0.05);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  animation: systemMessageAppear 0.6s cubic-bezier(0.215, 0.610, 0.355, 1.000) forwards;
  border-left: 3px solid #4cc9f0;
  border-right: 3px solid #7209b7;
}

.system-icon {
  color: #61dafb;
  font-size: 1.1em;
}

@keyframes systemMessageAppear {
  from { opacity: 0; transform: translateY(15px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}


.chat-container {
  display: flex;
  height: 100vh;
  background: linear-gradient(160deg, #10163a 0%, #1a1a2e 50%, #0a071f 100%);
  color: #e8e8f0;
  overflow: hidden;
  backdrop-filter: blur(5px);
}

.chat-sidebar {
  width: 340px;
  background: rgba(22, 20, 45, 0.75);
  backdrop-filter: blur(15px) saturate(180%) brightness(1.1);
  border-right: 1.5px solid rgba(255, 255, 255, 0.15);
  display: flex;
  flex-direction: column;
  padding: 25px;
  box-shadow: 10px 0 35px rgba(0, 0, 0, 0.35), inset -5px 0 15px rgba(0,0,0,0.1);
  z-index: 10;
  transition: width 0.3s ease;
  animation: sidebarOpen 0.7s cubic-bezier(0.23, 1, 0.320, 1) forwards;
}
@keyframes sidebarOpen{
  from { transform: translateX(-100%); opacity: 0; }
  to { transform: translateX(0); opacity: 1;}
}


.sidebar-header {
  text-align: center;
  margin-bottom: 30px;
  padding-bottom: 25px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

.app-title {
  font-size: 2.8rem;
  font-weight: 800;
  margin-bottom: 10px;
  color: #f5f5fa;
  cursor: default;
  letter-spacing: 1px;
  text-shadow: 0 0 10px rgba(247, 37, 133, 0.3), 0 0 20px rgba(72, 9, 183, 0.3);
}

.app-title .title-accent {
  background: linear-gradient(90deg, #f72585, #b5179e, #7209b7, #560bad);
  -webkit-background-clip: text;
  background-clip: text;
  background-size: 200% auto;
  color: transparent;
  animation: textShine 5s linear infinite alternate, hueRotate 15s infinite linear;
}

.app-subtitle {
  font-size: 0.95rem;
  color: rgba(220, 220, 255, 0.7);
  font-style: italic;
}

.user-info-section {
  display: flex;
  align-items: center;
  padding: 20px 10px;
  margin-bottom: 25px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  gap: 18px;
  background: rgba(255,255,255,0.03);
  border-radius: 10px;
}

.user-avatar-main {
  width: 75px;
  height: 75px;
  border-radius: 50%;
  overflow: hidden;
  flex-shrink: 0;
  position: relative;
  border: 3px solid rgba(255, 255, 255, 0.25);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
  box-shadow: 0 0 0 0px rgba(76,201,240,0);
}
.user-avatar-main:hover {
  transform: scale(1.08) rotate(5deg);
  box-shadow: 0 0 15px rgba(76,201,240,0.5), 0 0 8px rgba(247,37,133,0.3);
}
.user-avatar-main:hover {
  animation: pulse 1.5s infinite;
}


.avatar-img {
  width: 100%; height: 100%; object-fit: cover;
}
.avatar-default {
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
  background: linear-gradient(145deg, #7209b7, #f72585, #b5179e);
}
.avatar-initial-large {
  font-size: 2.2rem; color: white; font-weight: bold;
  text-shadow: 1px 1px 5px rgba(0,0,0,0.4);
}

.avatar-upload-overlay {
  position: absolute; bottom: 0; left: 0; width: 100%; height: 45%;
  background: rgba(0, 0, 0, 0.6); color: white;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.2rem; cursor: pointer; opacity: 0;
  transition: opacity 0.3s ease, background 0.3s ease;
  border-bottom-left-radius: 37px; border-bottom-right-radius: 37px;
}
.user-avatar-main:hover .avatar-upload-overlay {
  opacity: 1;
  background: rgba(0, 0, 0, 0.75);
}
.avatar-input-hidden { display: none; }

.user-details { flex-grow: 1; min-width: 0; }
.username-display {
  font-size: 1.15rem; font-weight: 600; color: #f0f0f5; margin-bottom: 5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.nickname-display, .nickname-edit-form {
  display: flex; align-items: center; font-size: 1rem;
  color: rgba(220, 220, 255, 0.75); gap: 10px;
}
.nickname-display span {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  flex-grow: 1;
}

.edit-action-btn, .confirm-action-btn, .cancel-action-btn {
  background: none; border: none; padding: 6px;
  border-radius: 50%; cursor: pointer; font-size: 1rem;
  transition: background-color 0.2s, color 0.2s, transform 0.2s;
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
}
.edit-action-btn { color: #4cc9f0; }
.edit-action-btn:hover { background-color: rgba(76, 201, 240, 0.25); transform: scale(1.15); }

.confirm-action-btn { color: #50c878; }
.confirm-action-btn:hover { background-color: rgba(80, 200, 120, 0.25); transform: scale(1.15); }

.cancel-action-btn { color: #ff6961; }
.cancel-action-btn:hover { background-color: rgba(255, 105, 97, 0.25); transform: scale(1.15); }


.nickname-input-field {
  flex-grow: 1; padding: 8px 12px; border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(0, 0, 0, 0.25); color: white; font-size: 0.95rem; min-width: 0;
  transition: border-color 0.3s, box-shadow 0.3s;
}
.nickname-input-field:focus {
  outline: none;
  border-color: #4cc9f0;
  box-shadow: 0 0 8px rgba(76,201,240,0.4), 0 0 15px rgba(114,9,183,0.3);
}

.online-users-section {
  margin-bottom: 25px; flex-grow: 1; display: flex; flex-direction: column; min-height: 0;
}

.section-title {
  font-size: 1.3rem; margin-bottom: 18px; padding-bottom: 15px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12); color: #7bdcb5;
  display: flex; align-items: center; gap: 10px;
  text-shadow: 0 0 5px rgba(123, 220, 181, 0.3);
}
.section-title .fa-users-cog {
  color: #f7a83e;
  animation: spinIcon 8s linear infinite;
}
@keyframes spinIcon { from {transform: rotate(0deg);} to {transform: rotate(360deg);} }


.user-list-scrollable {
  flex-grow: 1; overflow-y: auto; padding-right: 8px;
}
.no-users-text, .no-messages-prompt {
  text-align: center; color: rgba(255,255,255,0.55); padding: 25px; font-style: italic;
}
.no-messages-prompt {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  height: 100%; font-size: 1.15rem;
}
.no-messages-prompt i {
  font-size: 2.2rem; margin-bottom: 12px; color: rgba(255,255,255,0.35);
  animation: float 3s ease-in-out infinite;
}

.online-user-item {
  display: flex; align-items: center; padding: 12px 8px;
  border-radius: 10px; margin-bottom: 6px;
  transition: background-color 0.25s ease, opacity 0.3s ease, transform 0.2s ease, box-shadow 0.2s ease;
  cursor: default; position: relative;
}
.online-user-item:hover {
  background-color: rgba(255, 255, 255, 0.1);
  transform: translateX(5px);
  box-shadow: -3px 0 10px rgba(76,201,240,0.2);
}
.online-user-item:hover .user-action-indicator {
  opacity: 1;
  transform: translateX(0) scale(1.1);
}
.user-action-indicator {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%) translateX(10px) scale(0.8);
  opacity: 0;
  color: rgba(255,255,255,0.5);
  transition: opacity 0.3s ease, transform 0.3s ease, color 0.3s ease;
}
.online-user-item:hover .user-action-indicator:hover {
  color: #4cc9f0;
}


.user-avatar-small {
  width: 40px; height: 40px; margin-right: 15px; border-radius: 50%;
  overflow: hidden; flex-shrink: 0; position: relative;
  border: 2px solid transparent;
  transition: border-color 0.3s ease;
}
.avatar-small-img { width: 100%; height: 100%; object-fit: cover; }
.avatar-small-default {
  width: 100%; height: 100%; border-radius: 50%;
  background: linear-gradient(135deg, #4361ee, #3a0ca3, #560bad);
  display: flex; align-items: center; justify-content: center;
}
.avatar-initial-small { font-size: 1.05rem; color: white; font-weight: bold; }

.status-indicator {
  position: absolute; bottom: 0px; right: 0px;
  width: 12px; height: 12px; border-radius: 50%;
  background-color: #6c757d;
  border: 2.5px solid rgba(22, 20, 45, 0.9);
  transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
.status-indicator.status-online {
  background-color: #28f745;
  box-shadow: 0 0 8px #28f745, 0 0 3px #28f745;
}

.user-item-name {
  font-size: 1rem; color: #d8d8e0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.user-active .user-item-name {
  color: #f5f5fa; font-weight: 500;
  text-shadow: 0 0 3px rgba(255,255,255,0.2);
}
.user-active .user-avatar-small {
  border-color: #28f745;
  box-shadow: 0 0 5px #28f745;
}

.user-inactive { opacity: 0.65; }
.user-inactive:hover { opacity: 0.85; }

.sidebar-footer {
  margin-top: auto; padding-top: 25px;
  border-top: 1.5px solid rgba(255, 255, 255, 0.12);
}

.logout-action-button {
  width: 100%; padding: 15px;
  background: rgba(247, 37, 133, 0.25);
  border: 1.5px solid rgba(247, 37, 133, 0.45);
  color: #f72585; border-radius: 12px; font-size: 1.05rem;
  font-weight: 600;
  cursor: pointer; transition: all 0.3s ease;
  display: flex; align-items: center; justify-content: center; gap: 12px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.logout-action-button:hover {
  background: rgba(247, 37, 133, 0.4); color: white;
  transform: translateY(-3px) scale(1.02);
  box-shadow: 0 8px 20px rgba(247, 37, 133, 0.35), 0 0 15px rgba(247, 37, 133, 0.2) inset;
}
.logout-action-button i {
  transition: transform 0.3s ease;
}
.logout-action-button:hover i {
  transform: rotate(-15deg) scale(1.15);
}

.chat-main-area {
  flex: 1; display: flex; flex-direction: column;
  background: rgba(10, 8, 25, 0.5);
  backdrop-filter: blur(10px) brightness(0.95);
  position: relative; min-width: 0;
  border-left: 1px solid rgba(255, 255, 255, 0.08);
}

.message-list-container {
  flex: 1; overflow-y: auto; padding: 30px;
  display: flex; flex-direction: column; gap: 8px;
}

.chat-input-area {
  padding: 20px 30px;
  background: rgba(22, 20, 45, 0.85);
  border-top: 1.5px solid rgba(255, 255, 255, 0.15);
  box-shadow: 0 -8px 20px rgba(0,0,0,0.15);
}

.input-field-container {
  display: flex; align-items: flex-end; gap: 12px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 30px;
  padding: 8px 10px 8px 22px;
  border: 1.5px solid rgba(255, 255, 255, 0.15);
  transition: box-shadow 0.35s ease, border-color 0.35s ease;
  position: relative;
}
.input-field-container.focused {
  animation: iridescentBorder 3s infinite linear;
  box-shadow: 0 0 15px rgba(76, 201, 240, 0.5), 0 0 8px rgba(114,9,183,0.3);
}

.image-upload-button {
  background: none;
  border: none;
  color: #b0b8d4;
  font-size: 1.35rem;
  padding: 8px;
  margin: 0;
  cursor: pointer;
  transition: color 0.2s ease, transform 0.2s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 42px;
  height: 42px;
  border-radius: 50%;
}
.image-upload-button:hover:not(:disabled) {
  color: #4cc9f0;
  transform: scale(1.1);
}
.image-upload-button:disabled {
  color: #6c757d;
  cursor: not-allowed;
  opacity: 0.6;
}


.input-field-container textarea {
  flex: 1; padding: 12px 0;
  border: none; background: transparent; color: white;
  font-size: 1.05rem; font-family: inherit; resize: none;
  overflow-y: auto; max-height: 125px;
  line-height: 1.55; outline: none;
}

.input-field-container textarea::placeholder {
  color: rgba(255, 255, 255, 0.55);
  font-style: italic;
}

.send-action-button {
  width: 54px; height: 54px;
  border-radius: 50%;
  background: linear-gradient(135deg, #5f17f5, #c22fed);
  color: white; border: none; font-size: 1.3rem;
  cursor: pointer; transition: all 0.3s cubic-bezier(0.25, 1.5, 0.5, 1);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0; box-shadow: 0 5px 15px rgba(95, 23, 245, 0.3);
}
.send-action-button:disabled {
  background: rgba(95, 23, 245, 0.45);
  cursor: not-allowed; transform: none; box-shadow: none; opacity: 0.7;
  filter: grayscale(50%);
}
.send-action-button:not(:disabled):hover {
  transform: scale(1.15) rotate(20deg);
  box-shadow: 0 10px 25px rgba(95, 23, 245, 0.45);
  filter: brightness(1.1);
}
.send-action-button:not(:disabled):active {
  transform: scale(0.95) rotate(0deg);
  filter: brightness(0.85);
  box-shadow: 0 3px 10px rgba(95, 23, 245, 0.25);
}

.upload-error-message {
  color: #ff9a8c;
  font-size: 0.85rem;
  text-align: center;
  padding-top: 8px;
}

.image-modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.85);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  backdrop-filter: blur(8px);
  animation: fadeInModal 0.3s ease-out;
}

@keyframes fadeInModal {
  from { opacity: 0; }
  to { opacity: 1; }
}

.image-modal-content {
  position: relative;
  max-width: 90vw;
  max-height: 90vh;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 10px 30px rgba(0,0,0,0.5);
  animation: scaleUpModalContent 0.3s ease-out forwards;
  transform-origin: center center;
}

@keyframes scaleUpModalContent {
  from { transform: scale(0.8); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}


.modal-image-display {
  display: block;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: 8px;
}

.modal-close-button {
  position: absolute;
  top: -15px;
  right: -15px;
  background: rgba(30, 30, 30, 0.8);
  color: white;
  border: 2px solid rgba(255,255,255,0.5);
  border-radius: 50%;
  width: 40px;
  height: 40px;
  font-size: 2rem;
  font-weight: 300;
  line-height: 36px;
  text-align: center;
  cursor: pointer;
  transition: background-color 0.2s ease, transform 0.2s ease;
  box-shadow: 0 2px 10px rgba(0,0,0,0.3);
}

.modal-close-button:hover {
  background-color: rgba(247, 37, 133, 0.8);
  transform: scale(1.1) rotate(90deg);
}


@media (max-width: 768px) {
  .chat-sidebar {
    width: 100%; height: auto; max-height: 50vh;
    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
    padding: 20px;
  }
  .chat-container { flex-direction: column; }
  .user-info-section { padding: 15px 5px; }
  .user-avatar-main { width: 65px; height: 65px; }
  .app-title { font-size: 2.2rem; }
  .section-title { font-size: 1.15rem; }
  .message-list-container { padding: 20px 15px; }
  .chat-input-area { padding: 15px 20px; }
  .input-field-container { gap: 8px; padding: 8px 8px 8px 15px; }
  .image-upload-button { width: 38px; height: 38px; font-size: 1.2rem; }
  .input-field-container textarea { font-size: 1rem; }
  .send-action-button { width: 48px; height: 48px; font-size: 1.2rem; }
  .system-message { max-width: 90%; font-size: 0.85rem; }

  .modal-close-button {
    top: 5px;
    right: 5px;
    width: 32px;
    height: 32px;
    font-size: 1.5rem;
    line-height: 28px;
  }
  .image-modal-content {
    max-width: 95vw;
    max-height: 85vh;
  }
}
</style>
